package com.gz.controller;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.gz.common.Idempotent;
import com.gz.common.RedisConst;
import com.gz.common.UserStatus;
import com.gz.entity.Dto.RegisterRequest;
import com.gz.entity.Dto.UserDto;
import com.gz.entity.User;
import com.gz.entity.Vo.UserVo;
import com.gz.exception.Assert;
import com.gz.exception.BusinessCode;
import com.gz.exception.SystemCode;
import com.gz.service.UserService;
import com.gz.util.JwtUtil;
import com.gz.util.SmsUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;


@RestController
@Slf4j
@RequestMapping("/user")
@CrossOrigin
public class UserController {
    @Autowired
    UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisTemplate redisTemplate;

    //注入Rabbit模板类
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /*
     * 获取所有图书信息
     * */
    @PostMapping ("/login")
    @Idempotent  //使用幂等注解
    public ResponseEntity<?> doLogin(@RequestBody UserDto userDto, HttpServletRequest request) {
       ResponseEntity<?> responseEntity=  userService.doLogin(userDto);
       if (responseEntity.getStatusCode()== HttpStatus.OK) {
           User user = (User) responseEntity.getBody();
           return createTokenAndUserVo(request,user);
       }else {
           return responseEntity;
       }
    }

    /**
     * 生成Token，并且封装UserVo对象
     */
    private ResponseEntity<UserVo> createTokenAndUserVo(HttpServletRequest request, User user) {
        //1.生成Token
        HashMap<String, Object> data = new HashMap<>();
        data.put("userId", user.getId());
        data.put("userName", user.getAccount());
        //为了安全，判断IP地址是否相同
        data.put("ip", jwtUtil.getRequestIP(request));
        //生成Token
        String token = jwtUtil.createToken(data);
        log.info("用户ID：{} 登录成功，生成的Token为：{}", user.getId(), token);

        //2.存储到Redis中，保存1天
        String key = RedisConst.USER_TOKEN_PREFIX + ":" + user.getId();
        redisTemplate.opsForValue().set(key, token, Duration.ofDays(1));

        //3.封装UserVo对象
        UserVo userVo = new UserVo();
        //复制属性：源对象，目标对象
        BeanUtils.copyProperties(user, userVo);

        //4.以响应头的方式发送Token回去
        return ResponseEntity.ok()
                .header("Authorization", token)
                //告知浏览器可以访问 Authorization 响应头，否则访问不了
                .header("Access-Control-Expose-Headers", "Authorization")
                .body(userVo);
    }

    /**
     * 图片验证码
     */
    @GetMapping("/code")
//    @ApiOperation("生成图片验证码")
    public ResponseEntity generateCaptcha() throws IOException {
        //2) 创建缓存图片：指定宽
        int width = 110, height = 45;
        //参数3：图片的类型，使用RGB类型
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //3) 获取画笔对象
        Graphics graphics = image.getGraphics();
        //4) 设置画笔颜色，并且填充矩形区域
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        //创建一个StringBuider对象
        StringBuilder sb = new StringBuilder();
        //5) 从字符数组中随机得到字符
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        char[] arr = characters.toCharArray();

        Random random = new Random();

        for (int i = 0; i < 4; i++) {
            //随机从0到长度之间去取索引
            int index = random.nextInt(arr.length);
            //得到1个字符
            char c = arr[index];
            sb.append(c);
            //6) 设置字体，大小为22，设置字的颜色随机
            graphics.setFont(new Font(null, Font.BOLD + Font.ITALIC, 22));
            graphics.setColor(getColor());
            //7) 将每个字符画到图片，位置：5+(i*23), 25

            //将字符转成字符串
            graphics.drawString(String.valueOf(c), 5 + (i * 23), 25);
        }

        //8) 画干扰线8条线，线的位置是随机的，x范围在width之中，y的范围在height之中。
        for (int i = 0; i < 10; i++) {
            graphics.setColor(getColor());
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            graphics.drawLine(x1, y1, x2, y2);
        }
        //9) 将缓存的图片输出到响应输出流中
        //参数1：要输出的缓存图片，参数2：格式，参数3：输出到哪个流中
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "jpeg", outputStream);

        //随机生成一个key，前面加上code::
        String key = RedisConst.USER_VALIDATE_CODE + ":" + UUID.randomUUID().toString().replace("-", "");
        //得到验证码，将生成的字符串放到Redis中，键随机生成，10分钟过期
        redisTemplate.opsForValue().set(key, sb.toString(), Duration.ofMinutes(10));

        log.info("生成的验证码为：{}，Redis中的key是：{}", sb, key);

        Base64.Encoder encoder = Base64.getEncoder();
        String base64Img = "data:image/jpeg;base64," + encoder.encodeToString(outputStream.toByteArray());
        //返回的数据包含验证码的key和图片
        Map<String, String> data = new HashMap<>();
        data.put("key", key);  //redis中键
        data.put("base64Img", base64Img);  //图片
        //返回key和图片的base64的编码
        return ResponseEntity.ok(data);
    }

    //写一个方法随机获取颜色
    private Color getColor() {
        Random random = new Random();
        //红，绿，蓝取值0-255，随机生成 0-255之间数
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        return new Color(r, g, b);
    }


    @GetMapping("/logout/{id}")
    public ResponseEntity<?> logout(@PathVariable Integer id) {
        redisTemplate.delete(RedisConst.USER_TOKEN_PREFIX + ":" + id);
        return ResponseEntity.ok(SystemCode.SUCCESS_LOGOUT.getMessage());
    }


    @Autowired
    private SmsUtil smsUtil;

    /**
     - 手机号发短信
     */
    @GetMapping("/sendSms/{mobile}")
//    @ApiOperation("手机号发短信")
    public ResponseEntity<String> sendSms(@PathVariable String mobile) {
        //随机生成验证码，随机生成4个数字
        String code = RandomStringUtils.randomNumeric(4);
        log.info("短信验证码：{}", code);

        //给手机号发送短信，只需要测试一次，如果成功就没有必要每次发送
//        String result = smsUtil.sendSms(mobile, code);
//        String result = "OK";//返回正常，是OK；
        //把消息封装到一个Map中，给MQ
        Map<String, Object> smsData = new HashMap<>();
        smsData.put("code", code);
        smsData.put("mobile", mobile);
        // 发送消息到RabbitMQ队列中 参数1：队列名称 参数2：消息内容
        rabbitTemplate.convertAndSend("sms.queue", smsData);


//        Assert.error(!result.equals("OK"), BusinessCode.SMS_SEND_ERROR);

        //验证码放在Redis中，10分钟过期
        redisTemplate.opsForValue().set(RedisConst.SHORT_MESSAGE_SERVICE + ":" + mobile, code, Duration.ofMinutes(10));

        return ResponseEntity.ok(maskPhoneNumber(mobile)+SystemCode.SUCCESS_SMS.getMessage());
    }

    /**
     - 对电话号码进行脱敏处理
     */
    private String maskPhoneNumber(String telephone) {
        // 将第4、5、6、7位替换为 *
        return telephone.substring(0, 3) + "****" + telephone.substring(7);
    }

    /**
     - 发短信登录或注册
     */
    @PostMapping("/smsLogin")
//    @ApiOperation("发短信登录或注册")
    public ResponseEntity<?> smsLogin(@RequestBody UserDto userDto, HttpServletRequest request) {
        //获取手机号
        String mobile = userDto.getMobile();

        //从Redis中获取手机验证码
        String smsCode = redisTemplate.opsForValue().get(RedisConst.SHORT_MESSAGE_SERVICE + ":" + mobile).toString();
        //判断验证码是否过期
        Assert.error(Objects.isNull(smsCode), BusinessCode.INVALID_VERIFICATION);
        //判断验证码是否正确
        String smsCode1 = userDto.getSmsCode();
        Assert.error(!smsCode1.equals(smsCode), BusinessCode.VERIFICATION_ERROR);

        //删除验证码
        redisTemplate.delete(RedisConst.SHORT_MESSAGE_SERVICE + ":" + mobile);

        //获取图片验证码的key
        String key = userDto.getKey();
        //得到Redis中验证码
        String code = redisTemplate.opsForValue().get(key).toString();
        //判断验证码是否过期
        Assert.error(code==null, BusinessCode.INVALID_VERIFICATION);

        //判断验证码是否正确
        Assert.error(!code.equalsIgnoreCase(userDto.getCode()), BusinessCode.VERIFICATION_ERROR);

        //删除Redis中的验证码,每次验证后验证码会被删除；
        redisTemplate.delete(key);


        //根据手机号查询用户信息
        User user = userService.findUserByMobile(mobile);
        //如果用户存在，判断状态是否正常
        Assert.error(Objects.nonNull(user) && !user.getState().equals(UserStatus.ACTIVE.getKey()), BusinessCode.USER_FROZEN_ERROR);
        //如果用户不存在，创建新用户
        if (Objects.isNull(user)) {
            user = new User();
            user.setMobile(mobile);  //手机号
            //创建默认密码：123
            String hashedPassword=BCrypt.hashpw("123");
            user.setPassword(hashedPassword);    //加密密码
            user.setAccount(mobile);   //账户名：手机号
            user.setNickname(mobile);
            user.setRealName(mobile);
            user.setState(UserStatus.ACTIVE.getKey());  //状态
            userService.save(user);
        }
        //不为空则登录成功
        return createTokenAndUserVo(request, user);
    }


    //发件人
    @Value("${spring.mail.username}")
    private String from;

    @Autowired
    private JavaMailSender mailSender;
    @CrossOrigin
    @GetMapping("/sendEmail")
//    @ApiOperation("发送邮件")
    public ResponseEntity<String> sendEmail(@RequestParam(value = "address") String address) {
        //判断邮箱是否已经注册
        Assert.error(userService.isEmailExist(address), BusinessCode.EMAIL_EXIST_ERROR);
        //发送邮件
//        SimpleMailMessage mail = new SimpleMailMessage();
//        mail.setFrom(from);//发件箱
//        mail.setTo(address);  //收件箱
//        mail.setSubject("小米书城：验证码");//邮件主题
//        mail.setText(context);//生成的验证码

        String text = RandomUtil.randomNumbers(6);
        String context = "注册的验证码是：" + text + ", 该验证码15分钟之内有效！";

        //把消息封装到一个Map中，给MQ
        Map<String, Object> emailData = new HashMap<>();
        emailData.put("from", from);
        emailData.put("to", address);
        emailData.put("subject", "小米书城：注册验证码");
        emailData.put("content", context);
        // 发送消息到RabbitMQ队列中 参数1：队列名称 参数2：消息内容
        rabbitTemplate.convertAndSend("email.queue", emailData);

//        //为了提升响应速度，开启一个后台线程发送邮件
//        new Thread(() -> {
//            //发送成功以后，测试的时候把这句话注释了，以免每次发送邮件，被拉黑
////            mailSender.send(mail);
//            //模拟发送邮件
//            try {
//                Thread.sleep(2000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            log.info("验证码{}，发送到{}", text, address);
//        }).start();


        //将数据存储在Redis中,时间设置为15分钟，邮箱地址是键
        redisTemplate.opsForValue().set(RedisConst.EMAIL_ADDRESS_PREFIX + ":" + address, text, RedisConst.EMAIL_CODE_EXPIRE, TimeUnit.MINUTES);
        return ResponseEntity.ok(SystemCode.SUCCESS_EMAIL.getMessage()+",验证码已经发送到" + address + "邮箱，请去查收！");
    }

    @PostMapping("/register")
//    @ApiOperation("用户注册")
    public ResponseEntity<?> register(@RequestBody RegisterRequest registerRequest, HttpServletRequest request) {
        //从Redis中获取验证码是否正确
        String redisCode = redisTemplate.opsForValue().get(RedisConst.EMAIL_ADDRESS_PREFIX + ":" + registerRequest.getEmail()).toString();
        //判断验证码是否过期
        Assert.error(Objects.isNull(redisCode), BusinessCode.INVALID_VERIFICATION);

        //判断验证码是否正确
        Assert.error(!redisCode.equals(registerRequest.getVerifyCode()), BusinessCode.VERIFICATION_ERROR);

        //判断用户名是否重复
        Assert.error(userService.isAccountExist(registerRequest.getAccount()), BusinessCode.USER_IS_EXIST);

        //判断手机是否重复
        Assert.error(userService.isMobileExist(registerRequest.getMobile()), BusinessCode.MOBILE_IS_EXIST);

        //创建用户对象
        User user = new User();
        //将注册信息复制到user对象中
        BeanUtils.copyProperties(registerRequest, user);

        //调用业务层注册
        User newUser = userService.register(user);

        //默认已经登录
        return createTokenAndUserVo(request, newUser);
    }


}
